package cn.rui.config;

import cn.rui.common.filter.CodeFilter;
import cn.rui.security.BrowserAuthenticationFailureHandler;
import cn.rui.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @author 徽州大都督
 * @date 2021/4/16
 */

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    private DataSource dataSource; // 数据源

    //定制无权限页面
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    //自定义过滤器
    @Autowired
    private CodeFilter codeFilter;

//    //认证失败的处理器
//    @Bean
//    public MyFalureHandler myFalureHandler(){
//        return new MyFalureHandler();
//    }
//    //退出的处理器
//    @Bean
//    public MyLogoutHandler myLogoutHandler(){
//        return new MyLogoutHandler();
//    }

    //注入加密对象
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
    }

    //用于设置rememberMe往数据库存储token
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository  = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository .setDataSource(dataSource); // 设置数据源

        //自动创建数据库表，使用一次后注释掉，不然会报错
        //jdbcTokenRepository.setCreateTableOnStartup (true);
        return jdbcTokenRepository ;
    }



    /**
     * 开启部分路径配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //单用户登录，如果有一个登录了，同一个用户在其他地方登录将前一个剔除下线
        //http.sessionManagement().maximumSessions(1).expiredSessionStrategy(new MyExpiredSessionStrategy ());

        //单用户登录，如果有一个登录了，同一个用户在其他地方不能登录
        //http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);

        http.addFilterBefore(codeFilter, UsernamePasswordAuthenticationFilter.class);  //添加过滤器

        http.authorizeRequests().
                antMatchers("/css/**","/font/**","/img/**","/js/**","/json/**",
                        "/layer/**","/user/login","/register","/login","/user/register","/session/invalid","/sys/**").permitAll()
                .anyRequest().authenticated()	//访问其他需要认证
                .and()
                .formLogin()
                .loginPage("/login")	//设置取代默认登录页
                .loginProcessingUrl("/login")	//当在登录页跳转该url时就触发Security的登录请求
                .successForwardUrl ("/user/jumpIndex") //设置登录成功页面，不要使用successForwardUrl("/admin/index")，可能会出现405错误，原因不知，巨坑！
//                .failureForwardUrl("/admin")
                .failureHandler(new BrowserAuthenticationFailureHandler ())  //登录失败处理器，账户密码错误
          //      .successHandler (new BrowserAuthenticationSuccessHandler ())  //登录成功处理器
                .permitAll()
                .and()
                .logout().logoutUrl("/user/logout").logoutSuccessUrl("/login")
      //          .logoutSuccessHandler (new BrowserLogoutSuccessHandler ())   //退出处理器
                .deleteCookies ("remember-me")
                .permitAll ()//设置触发注销的url以及注销后跳转的url
                .invalidateHttpSession(true)
                .permitAll()
                .and()
                .rememberMe()
                .rememberMeParameter ("remember-me")
                .tokenValiditySeconds(60*6).tokenRepository//设置rememberMe的保存时长,6分钟
                (persistentTokenRepository())       //把rememberMe信息存到数据库
                .and()
                .exceptionHandling ().accessDeniedHandler (accessDeniedHandler)
                .and ()
                .csrf()	//禁用csrf
                .disable()
                .sessionManagement ().invalidSessionUrl ("/login")  //session失效访问url
                ;
    }


    /**
     * 静态资源忽略
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**").antMatchers("/swagger-ui.html")
                .antMatchers("/doc.html")
                .antMatchers("/webjars/**")
                .antMatchers("/v2/**")
                .antMatchers("/swagger-resources/**");;

    }


    //解决无法抛出用户名不存在异常
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setHideUserNotFoundExceptions(false);
        provider.setUserDetailsService(userService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

}
